import {
    FC, ReactNode, useCallback, useRef, useEffect
} from 'react';
import { initShader } from '@utils/utils';
import styles from './demo5.module.less';

export interface Demo5Props {
    children?: ReactNode;
}

const Demo5: FC<Demo5Props> = () => {
    const canvasRef = useRef(null);

    const main = useCallback(async (canvas: HTMLCanvasElement) => {
        const gl = canvas.getContext('webgl');
        //
        const VERTEX_SHADER_SOURCE = `
            attribute vec4 aPosition;
            uniform mat4 mat;
            void main() {
                gl_Position = mat * aPosition;
                gl_PointSize = 10.0;
            }
        `;

        const FRAGMENT_SHADER_SOURCE = `
            precision mediump float;
            uniform vec4 uColor;
            void main() {
                gl_FragColor = uColor;
            }
        `;

        const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);

        const aPosition = gl.getAttribLocation(program, 'aPosition');
        const uColor = gl.getUniformLocation(program, 'uColor');
        const mat = gl.getUniformLocation(program, 'mat');
        gl.uniform4f(uColor, 1.0, 0.0, 0.0, 1.0);

        const getTranslateMatrix = (x = 0, y = 0, z = 0) => {
            return new Float32Array([
                1.0, 0.0, 0.0, 0.0,
                0.0, 1.0, 0.0, 0.0,
                0.0, 0.0, 1.0, 0.0,
                x, y, z, 1.0
            ]);
        };

        const getScaleMatrix = (x = 1, y = 1, z = 1) => {
            return new Float32Array([
                x, 0.0, 0.0, 0.0,
                0.0, y, 0.0, 0.0,
                0.0, 0.0, z, 0.0,
                0.0, 0.0, 0.0, 1.0
            ]);
        };

        const getRotateMatrix = (deg = 0) => {
            return new Float32Array([
                Math.cos(deg), Math.sin(deg), 0.0, 0.0,
                -Math.sin(deg), Math.cos(deg), 0.0, 0.0,
                0.0, 0.0, 1.0, 0.0,
                0.0, 0.0, 0.0, 1.0,
            ]);
        };

        const getMixMatrix = (A: Float32Array, B: Float32Array): Float32Array => {
            const result = new Float32Array(16);
            for (let i = 0; i < 4; i++) {
                result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3];
                result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7];
                result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11];
                result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15];
            }

            return result;
        };

        const points = new Float32Array([
            -0.5, -0.5,
            0.5, -0.5,
            0.0, 0.5
        ]);

        const buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
        gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(aPosition);

        let x = -1;
        let scale = 0.1;
        let deg = 0;
        const run = () => {
            x += 0.01;
            scale += 0.01;
            deg += 0.01;

            if (x > 1) {
                x = -1;
            }

            if (scale > 2.1) {
                scale = 0.1;
            }
            const matrix = getMixMatrix(getMixMatrix(getTranslateMatrix(x), getScaleMatrix(scale, scale)), getRotateMatrix(deg));
            gl.uniformMatrix4fv(mat, false, matrix);
            gl.drawArrays(gl.TRIANGLES, 0, 3);
            requestAnimationFrame(run);
        };

        run();

        return () => {
            console.log('remove');
        };
    }, []);

    useEffect(() => {
        let removeEvent = null;
        if (canvasRef.current) {
            removeEvent = main(canvasRef.current);
        }

        return () => {
            if (typeof removeEvent === 'function') {
                removeEvent?.();
            }
        };
    }, [main]);

    return (
        <div className={styles.demo5}>
            <canvas ref={canvasRef} width="600" height="400" />
        </div>
    );
};

export default Demo5;
